package com.vhall.component.plugin.common.filter;

import cn.hutool.core.map.MapUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.vhall.component.framework.common.model.AccessLogModel;
import com.vhall.component.framework.common.utils.HttpServletUtils;
import com.vhall.component.framework.common.utils.WebUtils;
import com.vhall.component.plugin.common.constant.HttpConstant;
import com.vhall.component.plugin.common.properties.ReqLogProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * API 访问日志 Filter
 *
 * @author Administrator
 */
@Slf4j
@RequiredArgsConstructor
public class RequestLogFilter extends OncePerRequestFilter {
    private final String RSP_SIMPLE = "simple";
    private final String RSP_NORMAL = "normal";
    private final ReqLogProperties reqProperties;

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        // 只过滤 API 请求的地址
        return !reqProperties.getEnabled();
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        // 获得开始时间
        Date beginTime = new Date();
        // 提前获得参数，避免 XssFilter 过滤处理
        Map<String, String> query = ServletUtil.getParamMap(request);
        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request);
        String body = HttpServletUtils.isJsonRequest(request)
                ? IOUtils.toString(requestWrapper.getBody(), request.getCharacterEncoding()) : null;

        try {
            // 继续过滤器
            chain.doFilter(requestWrapper, response);
            // 正常执行，记录日志
            logBuild(request, beginTime, query, body);
        } catch (Exception ex) {
            // 异常执行，记录日志
            throw ex;
        }

    }

    private void logBuild(HttpServletRequest request, Date beginTime, Map<String, String> queryMap, String body) {
        if (reqProperties.getEnabled()) {
            AccessLogModel accessLog = new AccessLogModel();
            try {
                // 处理用户信息
                accessLog.setUid(WebUtils.getLoginUserId(request));
                if (Objects.nonNull(reqProperties.getRsp()) && reqProperties.getRsp().getEnabled()) {
                    // 设置访问结果
                    Object result = WebUtils.getResult(request);
                    if (result != null && reqProperties.getRsp().getLogType().equals(RSP_SIMPLE)) {
                        try {
                            JSONObject rs = (JSONObject) JSONObject.toJSON(result);
                            Integer code = rs.getInteger(HttpConstant.CODE);
                            String msg = rs.getString(HttpConstant.MSG);
                            msg = StringUtils.isEmpty(msg) ? rs.getString(HttpConstant.MESSAGE) : msg;
                            rs.clear();
                            rs.put(HttpConstant.CODE, code);
                            rs.put(HttpConstant.MSG, msg);
                            accessLog.setResult(rs);
                        } catch (Exception ex) {
                            log.error("response data is not json:[{}]", result);
                        }
                    }
                    if (result != null && reqProperties.getRsp().getLogType().equals(RSP_NORMAL)) {
                        accessLog.setResult(result);
                    }
                }

                // 设置其它字段
                accessLog.setUrl(request.getRequestURI());
                String handler = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
                accessLog.setUrlMatch(handler);

                Map<String, String> headerMap = new HashMap<>(16);
                if (Objects.nonNull(reqProperties.getHeader()) && reqProperties.getHeader().getEnabled()) {
                    if (StringUtils.isNotEmpty(reqProperties.getHeader().getKeys())) {
                        Map<String, String> headers = ServletUtil.getHeaderMap(request);
                        Arrays.stream(reqProperties.getHeader().getKeys().split(","))
                                .forEach(item -> {
                                    headerMap.put(item, headers.get(item));
                                });
                    }
                }

                Map<String, Object> requestParams = MapUtil.<String, Object>builder()
                        .put("header", headerMap)
                        .put("query", queryMap)
                        .put("body", body)
                        .build();
                accessLog.setArgs(requestParams);
                accessLog.setMethod(request.getMethod());
                accessLog.setClientIp(ServletUtil.getClientIP(request));

                // 持续时间
                accessLog.setBegin(beginTime);
                accessLog.setEnds(new Date());
                long diff = accessLog.getEnds().getTime() - accessLog.getBegin().getTime();
                accessLog.setDuration((int) diff);


                log.info(JSON.toJSONString(accessLog));
            } catch (Throwable th) {
                log.error("[RequestLogFilter][url({}) log({}) 发生异常]", request.getRequestURI(), accessLog, th);
            }

        }
    }
}
